JavaScript is partly an object-oriented language.
To learn JavaScript, we got to learn the object-oriented parts of JavaScript.
In this article, we’ll look at functions.
Functions that Return Functions
Fnctions can return functions.
For instance, we can write:
function a() {
console.log('foo');
return function() {
console.log('bar');
};
}
We return a function and then we can call the function we return.
For instance, we can write:
a()();
Then first call a
, so 'foo'
is logged.
Then we call the returned function with the second parentheses, so we get 'bar'
logged.
Functions that Rewrites Itself
We should be aware that functions can rewrite themselves.
For instance, we can write:
function a() {
console.log('foo');
a = function() {
console.log('bar');
};
}
Then we get 'foo'
logged.
The first console log runs, then the function is reassigned.
We shouldn’t do that and linters will alert us if we do this.
Closures
Closures are functions that have other functions inside it.
The inner functions can access the outer function’s scope.
For instance, if we have:
function outer() {
let outerLocal = 2;
function inner() {
let innerLocal = 3;
return outerLocal + innerLocal;
}
return inner();
}
We have the inner
function with the innerLocal
variable.
This is only available within the inner
function.
It can also access the outer
function’s outerLocal
variable.
This is useful because we want to have private variables in our code.
We have outerLocal
and innerLocal
that are only available inner
.
outer
can’t access the variables of inner
.
So we have different levels of privacy with these functions.
Closures in a Loop
If we have a loop that looks like:
function foo() {
var arr = [],
i;
for (i = 0; i < 3; i++) {
arr[i] = () => i
}
return arr;
}
Then if we call it:
const arr = foo();
for (const a of arr){
console.log(a());
}
then we get 3 from all the console logs.
This is because i
is 3 when we assigned the added our function to arr
.
var
isn’t block-scoped, so we would get the last value of i
instead of what’s in the loop.
To get the value of i
from the loop heading to the function we assign, we write:
function foo() {
var arr = [],
i;
for (i = 0; i < 3; i++) {
((j) => {
arr[i] = () => j
})(i)
}
return arr;
}
Then we get 0, 1, and 2 as we expect.
We can also replace var
with let
to make i
block-scoped and avoid this issue.
Getter and Setter
Getter functions return values and setters sets values.
We can put the getter and setter functions into a closure so that we can keep them private.
For instance, we can write:
let getValue, setValue;
(() => {
let secretVal = 0;
getValue = () => {
return secretVal;
};
setValue = (v) => {
if (typeof v === "number") {
secretVal = v;
}
};
}());
And we run a function to assign the getValue
and setValue
functions with functions.
getValue
returns the value of secretVal
and setValue
sets it.
This way, we keep secretVal
secret.
Conclusion
Closures are great for various applications.
It’s mostly to keep variables private, and we can also use them to bind variables values the way we expect.